perm filename TMP.TMP[MF,ALS]1 blob
sn#760296 filedate 1984-07-12 generic text, type C, neo UTF8
COMMENT ⊗ VALID 00002 PAGES
C REC PAGE DESCRIPTION
C00001 00001
C00002 00002 @* Doing the labels.
C00032 ENDMK
C⊗;
@* Doing the labels.
Each ``character'' in the \.{GF} file is preceded by a number of special
commands that define labels, titles, rules, etc. We store these away,
to be considered later when the |boc| command appears. The |boc|
command establishes the size information by which labels and rules
can be positioned, so we spew out the label information as soon as
we see the |boc|. The gray pixels will be typeset after all the labels
for a particular character have been finished.
@ Here is the part of \.{GFtoDVI} that stores information preceding a~|boc|.
It comes into play when |cur_gf| is between |nop| and~|yyy|, inclusive.
@d font_change(#)==if fonts_not_loaded then
begin #; end
else print_nl('(Tardy font change will be ignored (byte ',
@.Tardy font change...@>
cur_loc:1,')')
@<Process a no-op command@>=
begin k←interpret_xxx;
case k of
no_operation: do_nothing;
title_font,label_font,gray_font:font_change(font_name[k]←cur_string;
font_area[k]←null_string;font_at[k]←0;init_str_ptr←str_ptr);
title_font+area_code,label_font+area_code,gray_font+area_code:@|
font_change(font_area[k-area_code]←cur_string;init_str_ptr←str_ptr);
title_font+at_code,label_font+at_code,gray_font+at_code:@|
font_change(font_at[k-at_code]←get_yyy;init_str_ptr←str_ptr);
rule_thickness_code:rule_thickness←get_yyy;
rule_code:@<Store a rule@>;
offset_code:@<Override the offsets@>;
title_code:@<Store a title@>;
null_string:@<Store a label@>;
end; {there are no other cases}
end
@ The following quantities are cleared just before reading the
\.{GF} commands pertaining to a character.
@<Glob...@>=
@!rule_thickness:scaled; {the current rule thickness
(zero means use the default)}
@!offset_x,@!offset_y:scaled; {the current offsets}
@!pre_min_x,@!pre_max_x,@!pre_min_y,@!pre_max_y:scaled;
{extreme values of coordinates preceding a character}
@ @<Initialize variables for the next character@>=
rule_thickness←0;
offset_x←0; offset_y←0;
pre_min_x←@'2000000000; pre_max_x←-@'2000000000;
pre_min_y←@'2000000000; pre_max_y←-@'2000000000;
@ @<Override...@>=
begin offset_x←get_yyy; offset_y←get_yyy;
end
@ Rules that will need to be drawn are kept in a linked list accessible
via |rule_ptr|, in last-in-first-out order. The tree-node area of memory
serves double duty; i.e., we use it for rules as well as for labels, even
those these nodes will never be part of the 2D tree.
@d x0==xl {starting |x| coordinate of a stored rule}
@d y0==yt {starting |y| coordinate (in scaled \MF\ pixels)}
@d x1==xr {ending |x| coordinate of a stored rule}
@d y1==yb {ending |y| coordinate of a stored rule}
@d rule_size==xx {thickness of a stored rule, in scaled points}
@<Glob...@>=
@!rule_ptr:tree_pointer; {top of the stack of remembered rules}
@ @<Initialize variables for the next character@>=
rule_ptr←null; max_node←0;
@ @<Store a rule@>=
begin p←get_avail; right[p]←rule_ptr; rule_ptr←p;@/
x0[p]←get_yyy; y0[p]←get_yyy; x1[p]←get_yyy; y1[p]←get_yyy;
if x0[p]<pre_min_x then pre_min_x←x0[p];
if x0[p]>pre_max_x then pre_max_x←x0[p];
if y0[p]<pre_min_y then pre_min_y←y0[p];
if y0[p]>pre_max_y then pre_max_y←y0[p];
if x1[p]<pre_min_x then pre_min_x←x1[p];
if x1[p]>pre_max_x then pre_max_x←x1[p];
if y1[p]<pre_min_y then pre_min_y←y1[p];
if y1[p]>pre_max_y then pre_max_y←y1[p];
rule_size[p]←rule_thickness;
end
@ Titles and labels are, likewise, stored temporarily in the tree area.
In this case the list is first-in-first-out.
Variables |title_tail| and |label_tail| point to the most recently inserted
title or label; variables |title_head| and |label_head| will
point to the beginning of the list.
@d lab_typ==left {the type of a stored label (|"1"..."6"|)}
@d label_head==right[0] {this is a coding trick; do you get it?}
@<Glob...@>=
@!label_tail:tree_pointer; {tail of the queue of remembered labels}
@!title_head,@!title_tail:tree_pointer; {head and tail of the queue for titles}
@ @<Initialize variables for the next char...@>=
title_head←null; title_tail←null; label_tail←0; root←null;
@ @<Store a title@>=
begin p←get_avail; info[p]←cur_string;
if title_head=null then title_head←p
else right[title_tail]←p;
title_tail←p;
end
@ @<Store a label@>=
if (label_type<"1")∨(label_type>"6") then
print_nl('Bad label type precedes byte ',cur_loc:1,'!')
@.Bad label type...@>
else begin p←get_avail; right[label_tail]←p; label_tail←p;@/
lab_typ[p]←label_type; info[p]←cur_string;@/
xx[p]←get_yyy; yy[p]←get_yyy;
if xx[p]<pre_min_x then pre_min_x←xx[p];
if xx[p]>pre_max_x then pre_max_x←xx[p];
if yy[p]<pre_min_y then pre_min_y←yy[p];
if yy[p]>pre_max_y then pre_max_y←yy[p];
end
@ The process of ferreting everything away comes to an abrupt halt
when a |boc| command is sensed. The following steps are performed
at such times:
@<Process a character@>=
begin check_fonts;
@<Finish reading the parameters of the |boc|@>;
@<Get ready to convert \MF\ coordinates to \.{DVI} coordinates@>;
@<Output the |bop| and the title line@>;
print('[',total_pages:1); update_terminal; {print a progress report}
@<Output all rules for the current character@>;
@<Output all labels for the current character@>;
do_pixels; dvi_out(eop); {finish the page}
@<Adjust the maximum page width@>;
print(']'); update_terminal;
end
@ @<Finish reading the parameters of the |boc|@>=
char_code←signed_quad; {read the character code}
k←signed_quad; {read and ignore the prev pointer}
min_x←signed_quad; {read the minimum $x$ coordinate}
max_x←signed_quad; {read the maximum $x$ coordinate}
min_y←signed_quad; {read the minimum $y$ coordinate}
max_y←signed_quad; {read the maximum $y$ coordinate}
z←signed_quad; {read the starting column}
if max_x<min_x then {get set for a degenerate character}
begin max_x←z; min_x←z; max_y←0; min_y←0;
end
else if max_x-min_x≥widest_row then abort('Character too wide!')
@.Character too wide@>
@ @<Glob...@>=
@!char_code:integer; {the current character code}
@!min_x,@!max_x,@!min_y,@!max_y:integer; {character boundaries, in pixels}
@!x,@!y,@!z:integer; {current painting position, in pixels}
@ \MF\ coordinates $(x,y)$ are converted to \.{DVI} coordinates by the
following routine. Real values |x_ratio|, |y_ratio|, and |slant_ratio|
will have been calculated based on the gray font; |scaled| values
|delta_x| and |delta_y| will have been computed so that, in the absence
of slanting and offsets, \MF\ point |(min_x,max_y+1)| will correspond
to \.{DVI} coordinate $(0,50\,\rm pt)$.
@p procedure convert(@!x,@!y:scaled);
begin dvi_y←-round(y_ratio*y)+delta_y;
dvi_x←round(x_ratio*x+slant_ratio*y)+delta_x;
end;
@ @<Glob...@>=
@!x_ratio,@!y_ratio,@!slant_ratio:real; {conversion factors}
@!unsc_x_ratio,@!unsc_y_ratio,@!unsc_slant_ratio:real;
{ditto, times |unity|}
@!fudge_factor:real; {unconversion factor}
@!delta_x,@!delta_y:scaled; {magic constants used by |convert|}
@!dvi_x,@!dvi_y:scaled; {outputs of |convert|, in scaled points}
@!over_col:scaled; {overflow labels start here}
@ @<Initialize global variables that depend on the font data@>=
i←char_info(gray_font)(1);
if ¬ char_exists(i) then abort('Missing pixel char!');
@.Missing pixel char@>
unsc_x_ratio←char_width(gray_font)(i);
x_ratio←unsc_x_ratio/unity;
unsc_y_ratio←char_height(gray_font)(height_depth(i));
y_ratio←unsc_y_ratio/unity;
unsc_slant_ratio←slant(gray_font)*y_ratio;
slant_ratio←unsc_slant_ratio/unity;
if x_ratio*y_ratio=0 then abort('Vanishing pixel size!');
@.Vanishing pixel size@>
fudge_factor←(slant_ratio/x_ratio)/y_ratio;
@ @<Get ready to convert...@>=
if pre_min_x<min_x*unity then offset_x←offset_x+min_x*unity-pre_min_x;
if pre_max_y>max_y*unity then offset_y←offset_y+max_y*unity-pre_max_y;
if pre_max_x>max_x*unity then pre_max_x←pre_max_x div unity
else pre_max_x←max_x;
if pre_min_y<min_y*unity then pre_min_y←pre_min_y div unity
else pre_min_y←min_y;
delta_y←round(unsc_y_ratio*(max_y+1)-y_ratio*offset_y)+3276800;
delta_x←round(x_ratio*offset_x-unsc_x_ratio*min_x);
if slant_ratio≥0 then
over_col←round(unsc_x_ratio*pre_max_x+unsc_slant_ratio*max_y)
else over_col←round(unsc_x_ratio*pre_max_x+unsc_slant_ratio*min_y);
over_col←over_col+delta_x+10000000;
k←round(unsc_y_ratio*(max_y+1-pre_min_y))+3276800-offset_y;
if k>max_v then max_v←k;
@ The |dvi_goto| subroutine outputs bytes to the \.{DVI} file that
will initiate typesetting at given \.{DVI} coordinates, assuming that
the current position of the \.{DVI} reader is $(0,0)$. This subroutine
begins by outputting a |push| command; therefore, a |pop| command should
be given later. That |pop| will restore the \.{DVI} position to $(0,0)$.
@p procedure dvi_goto(@!x,@!y:scaled);
begin dvi_out(push);
if x≠0 then
begin dvi_out(right4); dvi_four(x);
end;
if y≠0 then
begin dvi_out(down4); dvi_four(y);
end;
end;
@ @<Output the |bop| and the title line@>=
dvi_out(bop); incr(total_pages); dvi_four(total_pages); dvi_four(char_code);
for k←2 to 9 do dvi_four(0);
dvi_four(last_bop); last_bop←dvi_offset+dvi_ptr-45;@/
dvi_goto(0,655360); {the top baseline is 10\thinspace pt down}
if use_logo then
begin select_font(logo_font); hbox(small_logo,logo_font,true);
end;
select_font(title_font); hbox(time_stamp,title_font,true);@/
hbox(page_header,title_font,true); dvi_scaled(total_pages*65536.0);@/
hbox(char_header,title_font,true); dvi_scaled(char_code*65536.0);@/
if title_head≠null then
begin right[title_tail]←null;
repeat hbox(left_quotes,title_font,true);
hbox(info[title_head],title_font,true);
hbox(right_quotes,title_font,true);
title_head←right[title_head];
until title_head=null;
end;
dvi_out(pop)
@ @<Output all rules for the current character@>=
while rule_ptr≠null do
begin p←rule_ptr; rule_ptr←right[p];@/
if rule_size[p]=0 then rule_size[p]←gray_rule_thickness;
if rule_size[p]>0 then
begin convert(x0[p],y0[p]); temp_x←dvi_x; temp_y←dvi_y;
convert(x1[p],y1[p]);
if temp_x=dvi_x then @<Output a vertical rule@>
else if temp_y=dvi_y then @<Output a horizontal rule@>
else print_nl('Sorry, I can''t make diagonal rules');
@.Sorry, I can't...@>
end;
end
@ @<Glob...@>=
@!gray_rule_thickness:scaled; {thickness of rules, according to the gray font}
@!temp_x,@!temp_y:scaled; {temporary registers for intermediate calculations}
@ @<Initialize glob...@>=
gray_rule_thickness←default_rule_thickness(gray_font);
if gray_rule_thickness=0 then gray_rule_thickness←26214; {0.4\thinspace pt}
@ @<Output a vertical rule@>=
begin if temp_y>dvi_y then
begin k←temp_y; temp_y←dvi_y; dvi_y←k;
end;
dvi_goto(dvi_x-(rule_size[p] div 2), dvi_y);
dvi_out(put_rule); dvi_four(dvi_y-temp_y); dvi_four(rule_size[p]);
dvi_out(pop);
end
@ @<Output a horizontal rule@>=
begin if temp_x<dvi_x then
begin k←temp_x; temp_x←dvi_x; dvi_x←k;
end;
dvi_goto(dvi_x,dvi_y+(rule_size[p] div 2));
dvi_out(put_rule); dvi_four(rule_size[p]); dvi_four(temp_x-dvi_x);
dvi_out(pop);
end
@ Now we come to a more interesting part of the computation, where we
go through the stored labels and try to fit them in the illustration for
the current character, together with their associated dots.
While it would simplify font-switching if we were to typeset the labels
first, we find it desirable to typeset the dots first and then typeset the
labels. This procedure makes it possible for us to allow the dots to
overlap each other without allowing the labels to overlap. We will also arrange
to typeset all |lab_typ| 1, 2, 3, and 4 labels before the |lab_typ| 5 labels.
@<Output all labels for the current character@>=
overflow_line←1;
if label_tail≠0 then
begin right[label_tail]←null; select_font(gray_font);
p←right[0];
while p≠null do
begin
do_dot(p); p←right[p];
end;
select_font(label_font);
p←0;
while right[p]≠null do
begin q←right[p];
if lab_typ[q] < "5" then
begin
right[p]←right[q];
do_a_label(q);
end
else p←right[p];
end;
p←0;
while right[p]≠null do
begin q←right[p];
print_nl('In |do_b_labels|'); print(p); print(q);
q_save←right[q];
print_nl('more |do_b_labels|'); print(p); print(q); print (q_save);
{do_b_label(q);}
if dl_tie[q]=max_labels then right[p]←q_save else p←right[p];
end;
p←0;
while right[p]≠null do
begin p←right[p];
@<Typeset an overflow label for |p|@>;
end;
select_font(gray_font);
end;
@ @<Glob...@>=
@!overflow_line:integer; {the number of labels that didn't fit, plus~1}
@ A label that appears above its dot is considered to occupy a
rectangle of height~$h+\Delta$, depth~$d$, and width~$w+2\Delta$, where
$(h,w,d)$ are the height, width, and depth of the label computed by |hbox|,
and $\Delta$ is an additional amount of blank space that keeps labels from
coming too close to each other. (\.{GFtoDVI} arbitrarily defines $\Delta$
to be one half the width of a space in the label font.) This label is
centered over its dot, with its baseline $d+h'$ above the center of the dot;
here $h'=|dot_height|$ is the height of character~0 in the gray font.
Similarly, a label that appears below its dot is considered to occupy
a rectangle of height~$h$, depth~$d+\Delta$, and width~$w+2\Delta$; the
baseline is $h+h'$ below the center of the dot.
A label at the right of its dot is considered to occupy a rectangle of
height~$h+\Delta$, depth~$d+\Delta$, and width~$w+\Delta$. Its
reference point can be found by starting at the center of the dot and
moving right $w'=|dot_width|$ (i.e., the width of character~0 in the
gray font), then moving down by half the x-height of the label font.
A label at the left of its dot is similar.
A dot is considered to occupy a rectangle of height $2h'$ and width~$2w'$,
centered on the dot.
When the label type is |"1"|, |"2"|, |"3"|, or |"4"|, the labels
are put into the tree unconditionally. Otherwise they are put into the tree
only if they don't overlap any previously inserted rectangles.
@<Glob...@>=
@!delta:scaled; {extra padding to keep labels from being too close}
@!half_x_height:scaled; {amount to drop baseline of label below the dot center}
@!thrice_x_height:scaled; {baseline separation for overflow labels}
@!dot_width,@!dot_height:scaled; {$w'$ and $h'$ in the discussion above}
@ @<Initialize global variables that depend on the font data@>=
i←char_info(gray_font)(0);
if ¬ char_exists(i) then abort('Missing dot char!');
@.Missing dot char@>
dot_width←char_width(gray_font)(i);
dot_height←char_height(gray_font)(height_depth(i));
delta←space(label_font) div 2;
thrice_x_height←3*x_height(label_font);
half_x_height←thrice_x_height div 6;
@ Here is a subroutine that computes the rectangle boundaries
|xl[p]|, |xr[p]|, |yt[p]|, |yb[p]|, and the reference point coordinates
|xx[p]|,~|yy[p]|, for a label that is to be placed above a dot.
The coordinates of the dot's center are assumed given in |dvi_x|
and |dvi_y|; the |hbox| subroutine is assumed to have
already computed the height, width, and depth of the label box.
@p procedure top_coords(@!p:tree_pointer);
begin xx[p]←dvi_x-(box_width div 2); xl[p]←xx[p]-delta;
xr[p]←xx[p]+box_width+delta;@/
yb[p]←dvi_y-dot_height; yy[p]←yb[p]-box_depth;
yt[p]←yy[p]-box_height-delta;
end;
@ The other three label positions are handled by similar routines.
@p procedure bot_coords(@!p:tree_pointer);
begin xx[p]←dvi_x-(box_width div 2); xl[p]←xx[p]-delta;
xr[p]←xx[p]+box_width+delta;@/
yt[p]←dvi_y+dot_height; yy[p]←yt[p]+box_height;
yb[p]←yy[p]+box_depth+delta;
end;
@#
procedure right_coords(@!p:tree_pointer);
begin xl[p]←dvi_x+dot_width; xx[p]←xl[p]; xr[p]←xx[p]+box_width+delta;@/
yy[p]←dvi_y+half_x_height; yb[p]←yy[p]+box_depth+delta;
yt[p]←yy[p]-box_height-delta;
end;
@#
procedure left_coords(@!p:tree_pointer);
begin xr[p]←dvi_x-dot_width; xx[p]←xr[p]-box_width; xl[p]←xx[p]-delta;@/
yy[p]←dvi_y+half_x_height; yb[p]←yy[p]+box_depth+delta;
yt[p]←yy[p]-box_height-delta;
end;
@ Here are the procedures for locating the labels, for
adding them to the tree and for puting them out.
@p procedure do_a_label(@!p:tree_pointer);
var @!q:tree_pointer;
begin hbox(info[p],label_font,false); {Compute the size of this label}
dvi_x←xx[p]; dvi_y←yy[p];
case lab_typ[p] of
"1":top_coords(p);
"2":left_coords(p);
"3":right_coords(p);
"4":bot_coords(p);
end; {Only these four cases will be treated by |do_a_label|}
q←dl_tie[p]; dl_tie[q]←0; {to identify labelled dot for |nearest_dot| routine}
tree_ins(p);@/
dvi_goto(xx[p],yy[p]); hbox(info[p],label_font,true); dvi_out(pop);
end;
@ And here is the second of the two label routines.
@p procedure do_b_label(@!p:tree_pointer);
label found,exit;
var @!q:tree_pointer;
begin hbox(info[p],label_font,false); {Compute the size of this label}
dvi_x←xx[p]; dvi_y←yy[p];
@<Find non-overlapping coordinates, if possible;
otherwise set an overflow label and |return|@>;
found:tree_ins(p);@/
q←dl_tie[p]; dl_tie[q]←0; {to identify labelled dot for |nearest_dot| routine}
dvi_goto(xx[p],yy[p]); hbox(info[p],label_font,true); dvi_out(pop);
exit:end;
@ As noted earlier, the dots are all to be printed even when partly
overlapping. We can, therefore, enter them into the tree now and output them.
@p procedure do_dot(@!p:tree_pointer);
var @!q:tree_pointer;
begin
q←get_avail; dl_tie[p]←q; dl_tie[q]←p; info[q]←-info[p]; convert(xx[p],yy[p]);@/
xx[q]←dvi_x; yy[q]←dvi_y;@/
xx[p]←xx[q]; yy[p]←yy[q];@/
xl[q]←dvi_x-dot_width; xr[q]←dvi_x+dot_width;@/
yt[q]←dvi_y-dot_height; yb[q]←dvi_y+dot_height;@/
tree_ins(q);@/
dvi_goto(xx[q],yy[q]); dvi_out(0); dvi_out(pop);
end;
@ @<Find non-overlapping coordinates, if possible...@>=
dvi_x←xx[p]; dvi_y←yy[p];
top_coords(p);@+if ¬ overlap(p) then goto found;
left_coords(p);@+if ¬ overlap(p) then goto found;
right_coords(p);@+if ¬ overlap(p) then goto found;
bot_coords(p);@+if ¬ overlap(p) then goto found;
if lab_typ[p]="5" then dl_tie[p]←max_labels; {to identify this label for overflow treatment}
return;
@ @<Typeset an overflow label for |p|@>=
begin
xx[p]←dvi_x; yy[p]←dvi_y;
nearest_dot(q); incr(overflow_line);
dvi_goto(over_col,overflow_line*thrice_x_height+655360);
hbox(info[p],label_font,true);
hbox(equals_sign,label_font,true);
hbox(-info[q_dot],label_font,true);
hbox(plus_sign,label_font,true);
dvi_scaled((dvi_x-xx[q_dot])/x_ratio+(dvi_y-yy[q_dot])*fudge_factor);
dvi_out(",");
dvi_scaled((yy[q_dot]-dvi_y)/y_ratio);
dvi_out(")"); dvi_out(pop);
end
@ @<Adjust the maximum page width@>=
if overflow_line=1 then k←over_col-10000000
else k←over_col+10000000; {overflow labels are estimated to occupy $10↑7\,$sp}
if k>max_h then max_h←k